<?php
/**
 * Class to simplify using of secure Vkontakte API.
 *
 *
 * Changelog
 * 1.0
 * (i) Release
 *
 *
 * @author Alexander Zubakov <zubakov@xinit.ru>
 * @copyright © 2010 Alexander Zubakov
 * @version 1.0
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
 */
class vk_secapi {

    /**
     * Secret key you can get on Payment page when edit application
     * @var string
     */
    private $api_secret;

    /**
     * URL to send API requests
     * @var string
     */
    private $api_url = 'http://api.vkontakte.ru/api.php';

    /**
     * Standart parameters to use in API requests.
     * @var array
     */
    private $parameters;

    /**
     * stdClass with error of last API request or empty string if last
     * request was successful
     * @var mixed
     */
    private $error = '';

    /**
     * User-Agent to use in requests
     * @var string
     */
    private $user_agent = 'vk_secapi PHP Class (developer: http://xinit.ru/)';

    /**
     * Connection timeout used when send requests
     * @var int
     */
    private $conn_timeout = 5;


    /**
     * Calculate 'sig' parameter of API request.
     *
     * @param array $parameters Array of parameters to use for calculating
     *                          'sig' parameter.
     * @return string Value of parameter 'sig' to use in requets.
     */
    private function calc_sig($parameters) {

        //sort by parameters name
        ksort($parameters, SORT_STRING);

        //make string to calculate sig
        $sig = '';
        foreach ($parameters as $name => $value) {
            $sig .= "$name=$value";
        }
        $sig .= $this->api_secret;

        //calculate and return 'sig'
        return md5($sig);
    }

    /**
     * Send request to Vkontakte server and get answer.
     *
     * @param array $parameters Array of parameters to send to the
     *                          API-server.
     * @return string API-server response in plain text format.
     */
    private function api_request($parameters) {

        //send request using method POST
        $curl_handle = curl_init();
        curl_setopt_array(
            $curl_handle,
            array(
                CURLOPT_URL => $this->api_url,
                CURLOPT_CONNECTTIMEOUT => $this->conn_timeout,
                CURLOPT_POST => true,
                CURLOPT_USERAGENT => $this->user_agent,
                CURLOPT_POSTFIELDS => $parameters,
                CURLOPT_RETURNTRANSFER => true
            )
        );
        $answer = curl_exec($curl_handle);
        curl_close($curl_handle);

        return $answer;
    }


    /**
     * Create and initialize object.
     *
     * @param string $api_id Application ID which you can get on Editing
     *                       page when edit application.
     * @param string $api_secret Secret key you can get on Payment page when
     *                           edit application.
     */
    public function __construct($api_id, $api_secret) {
        $this->api_secret = $api_secret;

        //set standart parameters
        $this->parameters = array(
            'api_id' => $api_id,
            'format' => 'json',
            'v'      => '2.0'
        );
    }

    /**
     * Return response of last API request if it failed.
     *
     * @return mixed Object like stdClass with error code and error message
     *               or empty string like '' in case if there was no error
     *               in last request.
     *               Be carefull using result! It is error object itself, so
     *               you can't call $result->{'error'} but you need to call
     *               $result->{'error_code'}, $result->{'error_msg'}, etc.
     */
    public function get_last_error() {
        return $this->error;
    }

    /**
     * Call API method.
     *
     * @param string $method API function name you want to call.
     * @param array $parameters Associative array like 'name' => 'value'
     *                          with parameters of API function you call.
     *                          Never set parameters 'api_id', 'sig',
     *                          'timestamp', 'random', 'v' and 'format'
     *                          manually.
     * @return mixed Object like stdClass with parameters and values of
     *               API-server response or false in case of failure.
     */
    public function call($method, $parameters = array()) {

        //set some parameters automatically
        $parameters = array_merge($parameters, $this->parameters);

        $parameters['timestamp'] = time();
        $parameters['random']    = mt_rand();
        $parameters['method']    = $method;

        $parameters['sig']       = $this->calc_sig($parameters);

        //send request to API-provider server
        $response = $this->api_request($parameters);

        //decode response
        $response = json_decode($response);

        //error
        if (isset($response->{'error'})) {
            $this->error = $response->{'error'};
            return false;

        //success
        } else {
            $this->error = '';
            return $response;
        }
    }


    /**
     * Magic method allows you to call API functions directly as they are
     * methods of vk_secapi class. Replace symbol '.' (dot) with '_' when
     * write method name. For example, you can call API function
     * secure.sendNotification using 2 methods as shown below:
     *
     * //method call()
     * $result = $api->call(
     *     'secure.sendNotification',
     *     array(
     *         'uids' => '5042864',
     *         'message' => 'Message text'
     *     )
     * );
     *
     * //"magic" method
     * $result = $api->secure_sendNotification(
     *     array(
     *         'uids' => '5042864',
     *         'message' => 'Message text'
     *     )
     * );
     *
     * @param string $name Name of API function to call
     * @param array $arguments Array of arguments to use when call API
     *                         function
     */
    public function __call($name, $arguments) {
        $name = str_replace('_', '.', $name);
        return $this->call($name, (sizeof($arguments) > 0) ? $arguments[0] : array());
    }
}
